/*
 *
 * Interface to the Description Logic based referring expression generation.
 * Based on Alexander's code for the step-by-step generator for GIVE 1.
 *
 */
package edu.union.give2.simplenlgservernolandmarks;

import give.formula.Atom;
import give.nlgserver.discretize.Discretizer;
import give.nlgserver.discretize.RegionDiscretizer;
import give.world.World;
import grapht.GraphT;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.google.common.collect.*;

import dlgre.PositiveClassComputer;
import dlgre.formula.Formula;

public class REG {
    private final World world;
    private final Discretizer discretizer;

    private final Multimap<String, String> nearToMap;

    private final Map<String,String> predicates;

    private final Map<String, LexiconEntry> lexicon;

    protected REG(World world, Discretizer discretizer) {
        super();
        this.world = world;
        this.discretizer = discretizer;

	nearToMap = HashMultimap.create();
 	for ( String region : this.discretizer.getAllPositionNames() ) {
 	    List<String> regionObjects = ((RegionDiscretizer)this.discretizer).getRegionInformation(region).getNearObjects();

 	    for ( String o1 : regionObjects ) {
 		for ( String o2 : regionObjects ) {
 		    if ( ! o1.equals(o2)) {
 			nearToMap.put(o1, o2);
 			nearToMap.put(o2, o1);
 		    }
 		}
 	    }
	}

	/* map world predicates and relations to predicates and relations we 
	   want to use for REG; if we don't want to use the world predicates
	   directly
	*/
	predicates = new HashMap<String, String>();
	predicates.put("stateless-button", "button");


	/* map for verbalizing */
	lexicon = new HashMap<String, LexiconEntry>();
	lexicon.put("color-blue", new LexiconEntry("blue", "premod"));
	lexicon.put("color-green", new LexiconEntry("green", "premod"));
	lexicon.put("color-red", new LexiconEntry("red", "premod"));
	lexicon.put("nearto", new LexiconEntry("near to a", "rel"));
	lexicon.put("button", new LexiconEntry("button", "noun"));
	lexicon.put("trophy", new LexiconEntry("trophy", "noun"));
	lexicon.put("alarm", new LexiconEntry("alarm tile", "noun"));
	lexicon.put("door", new LexiconEntry("door", "noun"));
	lexicon.put("picture", new LexiconEntry("picture", "noun"));
	lexicon.put("safe", new LexiconEntry("safe", "noun"));
	lexicon.put("flower", new LexiconEntry("flower", "noun"));
	lexicon.put("couch", new LexiconEntry("couch", "noun"));
	lexicon.put("chair", new LexiconEntry("chair", "noun"));
	lexicon.put("lamp", new LexiconEntry("lamp", "noun"));

    }


    public String makeReferringExpression (String object, List<String> contrastSet)
    {
	Set<String> referrableObjects = new HashSet<String>(contrastSet);
	referrableObjects.add(object);
	
	Map<String, dlgre.formula.Formula> refexp = getReferringExpressions(referrableObjects);

	return realizeReferringExpression(object, refexp);	
    }


    private String realizeReferringExpression(String individual, Map<String,Formula> refexp) {
        Formula f = refexp.get(individual);

        if( f == null ) {
            return "<" + individual + ">";
        } else {
            return realizeReferringExpression(f.removeConjunctionsWithTop());
        }
    }

    private String realizeReferringExpression(Formula clean) {
        if( clean instanceof dlgre.formula.Literal ) {
            return lexicon.get(clean.prettyprint()).getWord();
        } else if( clean instanceof dlgre.formula.Conjunction ) {
            String noun = null;
            List<String> premod = new ArrayList<String>();
            List<String> postmod = new ArrayList<String>();
            List<String> relatedSubs = new ArrayList<String>();

            List<Formula> sub = ((dlgre.formula.Conjunction) clean).getSubAsJava();

            // collect all the pieces of the formula
            for( Formula s : sub ) {
                if( s instanceof dlgre.formula.Literal ) {
                    String it = s.prettyprint();

		    LexiconEntry le = lexicon.get(it);

                    if( le.getCat().equals("noun") ) {
                        noun = le.getWord();
                    } else if( le.getCat().equals("premod") ) {
                        premod.add(le.getWord());
                    } else if( le.getCat().equals("postmod") ) {
                        postmod.add(le.getWord());
                    }
                } else if( s instanceof dlgre.formula.Existential ) {
                    dlgre.formula.Existential e = (dlgre.formula.Existential) s;
                    relatedSubs.add(lexicon.get(e.role()).getWord() + " " + realizeReferringExpression(e.sub()));
                }
            }

            // put a noun into Cairo
            if( noun == null ) {
		    noun = "thing";
            }

            // assemble the RE
            StringBuffer buf = new StringBuffer("");

            for( String pre : premod ) {
                buf.append(" " + pre);
            }

            buf.append(" " + noun);

            for( String post : postmod ) {
                buf.append(" " + post);
            }

            for( String rel : relatedSubs ) {
                buf.append(" " + rel);
            }

            return buf.toString();
        } 
	else if (clean.prettyprint().equals("T")) {
	    return "thing";
	}
	else {
            return "thing I don't really know how to describe [" + clean.prettyprint() + "]";
        }
    }

    @SuppressWarnings("unchecked")
    private Map<String,dlgre.formula.Formula> getReferringExpressions(Collection<String> contrastSet) {
        GraphT g = makeGraph(world, contrastSet);
        return new PositiveClassComputer(g).computeForJava();
    }


    private String getIfKeyExists (Map<String, String> mapping, String key) {
	if (mapping.containsKey(key)) {
	    return mapping.get(key);
	} else {
	    return key;
	}
    }


    /* visibleObjects is really the contrastSet */
    private GraphT makeGraph(World world, Collection<String> visibleObjects) {
        GraphT ret = new GraphT();

        for( String i : visibleObjects ) {
            ret.addNode(i);

            if( world.getType(i) != null ) {
		ret.addPredicate(i, getIfKeyExists(predicates, world.getType(i)));
            }

	    for ( String o : nearToMap.get(i) ) {
		if ( visibleObjects.contains(o) ) {
		    ret.addEdge(i, o, "nearto");
		}
	    }
        }


        for( Atom at : world.getTrueAtoms() ) {
            if( (at.getArguments().size() == 1) && visibleObjects.contains(at.getArgumentAsString(0)) ) {
		if ( lexicon.containsKey(getIfKeyExists(predicates, at.getPredicate())) ) {
		    ret.addPredicate(at.getArgumentAsString(0), getIfKeyExists(predicates, at.getPredicate()));
		}
            } else if( (at.getArguments().size() == 2)
                    && visibleObjects.contains(at.getArgumentAsString(0))
                    && visibleObjects.contains(at.getArgumentAsString(1)) ) {
		if ( lexicon.containsKey(getIfKeyExists(predicates, at.getPredicate())) ) {
		    ret.addEdge(at.getArgumentAsString(0), at.getArgumentAsString(1), getIfKeyExists(predicates, at.getPredicate()));
		}
            } else if( at.getPredicate().equals("color")
                        && visibleObjects.contains(at.getArgumentAsString(0)) ) {
		if ( lexicon.containsKey(getIfKeyExists(predicates, "color-" + at.getArgumentAsString(1))) ) {
		    ret.addPredicate(at.getArgumentAsString(0), getIfKeyExists(predicates, "color-" + at.getArgumentAsString(1)));
		}
            }
        }

        return ret;
    }



    private class LexiconEntry {

	private String word;
	private String cat;

	public LexiconEntry(String w, String c) {
	    word = w;
	    cat = c;
	}

	public String getWord() {
	    return word;
	}
	public String getCat() {
	    return cat;
	}

    }


}
